// Written by Suman Jana and Brian McCord
//
// Portions of the face detection code are based on
// http://opencvfacedetect.blogspot.com/2010/10/face-detectionfollowed-by-eyesnose.html
// For more information contact the above authors
//
// NOTE: All images used must be 512 x 512 to guarantee results

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

#include "FreeImage.h"
#include "vecmath.h"
#include "morph.h"

#include "cv.h"
#include "highgui.h"
#include "cvaux.h"

#define N_VECS  100
#define PARAMS_FILENAME "params"

CvHaarClassifierCascade *cascade, *cascade_eyes, *cascade_nose, *cascade_mouth;
CvMemStorage *storage;
char *face_cascade="haarcascade_frontalface_alt2.xml";
char *eye_cascade="haarcascade_mcs_eyepair_big.xml";
char *nose_cascade="haarcascade_mcs_nose.xml";
char *mouth_cascade="haarcascade_mcs_mouth.xml";

FILE *editorFile;
FILE *editorFile2;
char *srcFilename;      
char *dstFilename;
char *vecsFilename;
char *finalFilename;
float percent;
char *faceDetect;
int bgnum;
int eyes_written, nose_written, mouth_written;

static char filecopy[1000000];

typedef struct _params {
        float a;
        float p;
        float b;
}params;

typedef struct _morphContext {
        vec *src_vecs;
        vec *dst_vecs;
        vec *intermed_vecs;
        int numOfVecs;
        params mParams;
        FIBITMAP *srcPic;
        FIBITMAP *dstPic;

} morphContext;


/**
 * Generic image loader
 * @param lpszPathName Pointer to the full file name
 * @param flag Optional load flag constant
 * @return Returns the loaded dib if successful, returns NULL otherwise
 * */
FIBITMAP *GenericLoader(const char* lpszPathName, int flag) {

        FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
        // check the file signature and deduce its format
        // (the second argument is currently not used by FreeImage)
        fif = FreeImage_GetFileType(lpszPathName, 0);
        if(fif == FIF_UNKNOWN) {
                // no signature ?
                // try to guess the file format from the file extension
                fif = FreeImage_GetFIFFromFilename(lpszPathName);
        }

        // check that the plugin has reading capabilities ...
        if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) {
                // ok, let's load the file
                FIBITMAP *dib = FreeImage_Load(fif, lpszPathName, flag);
                // unless a bad file format, we are done !
                return dib;
        }

        return NULL;
}

/**
 * Generic image writer
 * @param dib Pointer to the dib to be saved
 * @param lpszPathName Pointer to the full file name
 * @param flag Optional save flag constant
 * @return Returns true if successful, returns false otherwise
 * */
int GenericWriter(FIBITMAP* dib, const char* lpszPathName, int flag) {

        FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
        BOOL bSuccess = FALSE;

        // Try to guess the file format from the file extension
        fif = FreeImage_GetFIFFromFilename(lpszPathName);

        if(fif != FIF_UNKNOWN ) {

                // Check that the dib can be saved in this format
                BOOL bCanSave;
                FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
                if(image_type == FIT_BITMAP) {
                        // standard bitmap type
                        // check that the plugin has sufficient writing
                        // and export capabilities ...
                        WORD bpp = FreeImage_GetBPP(dib);
                        bCanSave = (FreeImage_FIFSupportsWriting(fif) &&
                        FreeImage_FIFSupportsExportBPP(fif, bpp));
                } else {
                        // special bitmap type
                        // check that the plugin has sufficient export capabilities
                        bCanSave = FreeImage_FIFSupportsExportType(fif, image_type);
                }
                if(bCanSave) {
                        bSuccess = FreeImage_Save(fif, dib, lpszPathName, flag);
                }
        }

        return (bSuccess == TRUE) ? 1 : 0;
}


int initParams(params *mParams) {
        mParams->a = 0.5;
	mParams->b = 1.0;
	mParams->p = 0.2;

	return 1;
}

/**
 * @param filep file to read from
 * @param vec the vector
 *
 * @return 1 on success,0 otherwise
 * */
int readVec(FILE *filep, vec *v) {
        int ret;
	int curoffset=0;
	char buf[200];
	
	/* beginning of new backgorund */
	curoffset=ftell(filep);
	if (fscanf(filep, "background%s\n", buf)==1) {
		fseek(filep, curoffset, SEEK_SET);
		return 0;
	}
	//fseek(filep, curoffset, SEEK_SET);

        ret = fscanf(filep, "line=%f,%f:%f,%f\n", &v->p.x, &v->p.y, &v->q.x, &v->q.y);
	
        return (ret == 4);
}

/**
 * @param filep file to read from
 * @param vecs the vector array
 *
 * @return number of vector read
 * */
int readSrcVecs(FILE *filep, vec **vecs) {
        int i=0;
        int numOfVecs = N_VECS;
	char fname[100];

	memset(fname, 0, sizeof(fname));
	if (fscanf(filep, "background1=%s\n", fname)==1) {
		while(readVec(filep, (*vecs)+i )) {
			i++;
			if(i == numOfVecs) {
				numOfVecs += N_VECS;
				*vecs = (vec*)realloc(*vecs, sizeof(vec)*numOfVecs);
			}
		}
	}

        return i;
}

int readDstVecs(FILE *filep, vec **vecs) {
        int i=0;
        int numOfVecs = N_VECS;
	char fname[100];

	memset(fname, 0, sizeof(fname));
	if (fscanf(filep, "background2=%s\n", fname)==1) {
		while(readVec(filep, (*vecs)+i )) {
			i++;
			if(i == numOfVecs) {
				numOfVecs += N_VECS;
				*vecs = (vec*)realloc(*vecs, sizeof(vec)*numOfVecs);
			}
		}
	}

        return i;
}

int initContext(const char *imgsrcfname,const char *imgdstfname,
                       const char *vfname, morphContext *context) 
{

        FILE *vecs_fp;

        memset(context, sizeof(*context), 0);

        // open vectors file
        if(!(vecs_fp = fopen(vfname, "r"))) {
                DIE("Error");
                return 0;
        }
        
	context->src_vecs = (vec *)malloc(N_VECS*sizeof(vec));
        context->numOfVecs = readSrcVecs(vecs_fp, &context->src_vecs);

        context->dst_vecs = (vec *)malloc(N_VECS*sizeof(vec));
        if( context->numOfVecs != readDstVecs(vecs_fp, &context->dst_vecs)) {
                DIE("Error");
                return 0;
        }
        
	fclose(vecs_fp);

        context->intermed_vecs = (vec *)malloc(context->numOfVecs*sizeof(vec));


        if(!initParams(&context->mParams)) {
                DIE("Error");
                return 0;
        }

        context->srcPic = GenericLoader(imgsrcfname, 0);
        if (!context->srcPic) {
                DIE("Error");
                return 0;
        }

        context->dstPic = GenericLoader(imgdstfname, 0);;
        if (!context->dstPic) {
                DIE("Error");
                return 0;
        }

        return 1;

}

void cleanContext(morphContext *context) {
        FreeImage_Unload(context->srcPic);
        FreeImage_Unload(context->dstPic);

        free(context->src_vecs);
        free(context->dst_vecs);
        free(context->intermed_vecs);
}


point *getMidPoint(vec *srcVec, vec *dstVec, float percent) {

        point *p = (point *)malloc(sizeof(point));
        point p2;

        // srcP + srcQ
        point_add(&srcVec->p, &srcVec->q, p);
        //0.5*(1-percent)*(P - Q)
        point_scale(p, 0.5f*(1.0f-percent), p);

        // dstP + dstQ
        point_add(&dstVec->p, &dstVec->q, &p2);
        // 0.5*percent*(dstP + dstQ)
        point_scale(&p2, 0.5f*percent, &p2);

        // 0.5*(1-percent)*(P - Q) + 0.5*percent*(dstP + dstQ)
        point_add(p,&p2,p);
        return p;
}

float getLength(vec *srcVec, vec *dstVec, float percent) {

        point p;
        float l1,l2;

        // srcQ - srcP
        point_sub(&srcVec->q, &srcVec->p, &p);

        // ||srcQ - srcP||*(1-percent)
        l1 = point_norm(&p)*(1.0f-percent);

        // dstQ - dstP
        point_sub(&dstVec->q, &dstVec->p, &p);

        // ||dstQ - dstP||*percent
        l2 = point_norm(&p)*percent;

        // ||srcQ - srcP||*(1-percent) + ||dstQ - dstP||*percent
        return l1+l2;
}

point *getDirection(vec *srcVec, vec *dstVec, float percent) {

         point *p = (point *)malloc(sizeof(point));
         point p2;
         // srcQ - srcP
         point_sub(&srcVec->q, &srcVec->p, p);

         // (srcQ - srcP)/||srcQ - srcP|| * (1 - percent)
         point_scale(p, (1.0-percent)/point_norm(p), p);

         // dstQ - dstP
         point_sub(&dstVec->q, &dstVec->p, &p2);

         // (dstQ - dstP)/||dstQ - dstP|| * percent
         point_scale(&p2, percent/point_norm(&p2), &p2);

          // (srcQ - srcP)/||srcQ - srcP|| * (1 - percent) +
          // (dstQ - dstP)/||dstQ - dstP|| * percent
         point_add(p,&p2,p);

         return p;
}

void getIntermedVecs(morphContext *context, float percent) {
        int i;
        point t1;

        for(i=0; i<context->numOfVecs; i++) {

                point *mid_p = getMidPoint(&context->src_vecs[i], &context->dst_vecs[i], percent);
                point *dir_p = getDirection(&context->src_vecs[i], &context->dst_vecs[i], percent);
                float len = getLength(&context->src_vecs[i], &context->dst_vecs[i], percent);

                point_scale(dir_p, len/2.0f, &t1);
                point_add(mid_p, &t1, &context->intermed_vecs[i].q);

                point_scale(dir_p, -len/2.0f, &t1);
                point_add(mid_p, &t1, &context->intermed_vecs[i].p);

        }

}

void findSource(point *x_intermed, vec *PQ, vec *PQtag, point *x_res, float *u, float *v) {
        point t1;
        point X_P,Q_P,perpQ_P;
        point Q_Ptag, perpQ_Ptag, uQ_Ptag;
        point v_perpQ_Ptag;
        // X-P
        point_sub(x_intermed, &PQ->p, &X_P);

        // Q-P
        point_sub(&PQ->q, &PQ->p, &Q_P);

        // point_dot(&t2, &t2) == ||Q - P||^2
        *u = point_dot(&X_P, &Q_P) / point_dot(&Q_P, &Q_P);

        // Perpendicular(Q-P)
        point_perp(&Q_P, &perpQ_P);

        // (X-P)*Perpendicular(Q-P) / || Q-P||
        *v = point_dot(&X_P, &perpQ_P)/point_norm(&Q_P);

        // Q'-P'
        point_sub(&PQtag->q, &PQtag->p, &Q_Ptag);

        // u*(Q'-P')
        point_scale(&Q_Ptag, *u, &uQ_Ptag);

        // Perpendicular(Q'-P')
        point_perp(&Q_Ptag, &perpQ_Ptag);

        // v*Perpendicular(Q'-P') / || Q' - P'||
        point_scale(&perpQ_Ptag, (*v)/point_norm(&Q_Ptag), &v_perpQ_Ptag);


        // u*(Q'-P') + v*Perpendicular(Q'-P') / ||Q'-P'||
        point_add(&uQ_Ptag, &v_perpQ_Ptag, &t1);

        // P' + u*(Q'-P') + v*Perpendicular(Q'-P') / ||Q'-P'||
        point_add(&PQtag->p, &t1, x_res);

}

FIBITMAP *warp(FIBITMAP * srcPic, vec *src_vecs, morphContext *context, int isSave, char *name) {
        int x, y, i;
        int numOfVecs = context->numOfVecs;

        int img_width = FreeImage_GetWidth(srcPic);
        int img_height = FreeImage_GetHeight(srcPic);

        float cp = context->mParams.p;
        float ca = context->mParams.a;
        float cb = context->mParams.b;

        point DSUM = { 0.0, 0.0 };
        point D = { 0.0, 0.0 }, DD =  { 0.0, 0.0 };

        float weight = 0.0f, weightsum = 0.0f;

        float dist=0, length=0;
        float u,v;

        // the intermediate interpolated point
        point x_intermed;

        // the point on the original image (mapped backwards)
        point x_orig_res;
        point Xtag;
        // Allocate a 24-bit dib
        FIBITMAP *dib = FreeImage_Allocate(img_width,img_height, 24,
                                                       FI_RGBA_RED_MASK,
                                                       FI_RGBA_GREEN_MASK,
                                                       FI_RGBA_BLUE_MASK );

        for(y = 0; y < img_height; y++) {
                for(x = 0; x < img_width; x++) {

                        DSUM.x = 0.0f; DSUM.y = 0.0f;
                        weightsum = 0.0f;

                        for(i=0; i<numOfVecs; i++) {

                                x_intermed.x = (float)x+0.5;
                                x_intermed.y = (float)y+0.5;

                                // calculate u,v and X'i ( ==  x_orig_res)
                                findSource(&x_intermed, &context->intermed_vecs[i], &src_vecs[i],
                                                        &x_orig_res , &u, &v);

                                // calculate displacement Di
                                point_sub(&x_orig_res, &x_intermed, &D);

                                if(u>=0.0 && u<=1.0) {
                                        dist = fabs(v);
                                }
                                else if(u<0.0) {
                                        dist = point_dist(&x_intermed, &context->intermed_vecs[i].p);
                                }
                                else if(u>1.0){
                                        dist = point_dist(&x_intermed, &context->intermed_vecs[i].q);
                                }

                                length = point_dist(&context->intermed_vecs[i].p, &context->intermed_vecs[i].q);

                                weight = powf(powf(length, cp) / (ca + dist), cb);

                                // Di*weight
                                point_scale(&D, weight, &DD);

                                // DSUM += Di*weight
                                point_add(&DSUM, &DD, &DSUM);

                                weightsum += weight;

                        }
                        if(weightsum != 0.0) {

                                point_scale(&DSUM, 1.0f/weightsum, &DSUM);
                                point_add(&x_intermed, &DSUM, &Xtag);
                        }
                        else {
                                Xtag.x = x_intermed.x;
                                Xtag.y = x_intermed.y;
                        }
/*
                        if(Xtag.x < 0 || Xtag.x >= img_width) {
                                Xtag.x = img_width - (((int)Xtag.x + img_width) % img_width) - 1;
                        }

                        if (Xtag.y < 0 || Xtag.y >= img_height) {
                                Xtag.y = img_height - (((int)Xtag.y + img_height) % img_height) - 1;
                        }
*/
                        RGBQUAD color;

                        FreeImage_GetPixelColor(srcPic, (int)Xtag.x, (int)Xtag.y, &color);

                        FreeImage_SetPixelColor(dib, x, y, &color);

                }
        }

        if(isSave) {
                FreeImage_Save(FIF_BMP, dib, name, BMP_DEFAULT);
        }

        return dib;
}

FIBITMAP *crossDissolve(FIBITMAP *src, FIBITMAP *dst, float percent) {

        int x,y;
        int img_width = FreeImage_GetWidth(src);
        int img_height = FreeImage_GetHeight(src);

        // Allocate a 24-bit dib
        FIBITMAP *res = FreeImage_Allocate(img_width,img_height, 24,
                                                                        FI_RGBA_RED_MASK,
                                                                        FI_RGBA_GREEN_MASK,
                                                                        FI_RGBA_BLUE_MASK );

        for(y = 0; y < img_height; y++) {

                BYTE *res_bits = FreeImage_GetScanLine(res, y);
                BYTE *src_bits = FreeImage_GetScanLine(src, y);
                BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

                for(x = 0; x < img_width; x++) {
                        res_bits[FI_RGBA_RED] = (int)(src_bits[FI_RGBA_RED]*(1.0f-percent) + dst_bits[FI_RGBA_RED]*percent);
                        res_bits[FI_RGBA_GREEN] = (int)(src_bits[FI_RGBA_GREEN]*(1.0f-percent) + dst_bits[FI_RGBA_GREEN]*percent);
                        res_bits[FI_RGBA_BLUE] = (int)(src_bits[FI_RGBA_BLUE]*(1.0f-percent) + dst_bits[FI_RGBA_BLUE]*percent);

                        res_bits += 3;
                        src_bits += 3;
                        dst_bits += 3;
                }

        }

        return res;
}

/* --------------------------------
*  OPENCV FEATURES
*  -------------------------------- */

/* Write a CvRect rectangle to the line editor */
void rec2edfile(CvRect *rec, CvRect *r){
	int ulx,uly, urx, ury, lrx, lry, llx, lly;
	ulx = rec->x + r->x;
	uly = 512 - (rec->y + r->y);
	urx = rec->x + rec->width + r->x;
	ury = 512 - (rec->y + r->y);
	lrx = rec->x + rec->width + r->x;
	lry = 512 - (rec->y + rec->height + r->y);
	llx = rec->x + r->x;
	lly = 512 - (rec->y + rec->height + r->y);
	if (bgnum == 0) {	
	fprintf( editorFile, "line=%d,%d:%d,%d\n", ulx, uly, urx, ury);
	fprintf( editorFile, "line=%d,%d:%d,%d\n", urx, ury, lrx, lry);
	fprintf( editorFile, "line=%d,%d:%d,%d\n", lrx, lry, llx, lly);
	fprintf( editorFile, "line=%d,%d:%d,%d\n", llx, lly, ulx, uly );
	} else if (bgnum == 1) {
	fprintf( editorFile2, "line=%d,%d:%d,%d\n", ulx, uly, urx, ury);
	fprintf( editorFile2, "line=%d,%d:%d,%d\n", urx, ury, lrx, lry);
	fprintf( editorFile2, "line=%d,%d:%d,%d\n", lrx, lry, llx, lly);
	fprintf( editorFile2, "line=%d,%d:%d,%d\n", llx, lly, ulx, uly );
	} else {
	printf("ERROR:  Cannot write rectangle to file");
	}
}

/*Mouth detection*/
void detectMouth( IplImage *img,CvRect *r)
{
   CvSeq *mouth;
   //mouth detecetion - set region of interest (ROI)
   cvSetImageROI(img,			/* the source image */
                 cvRect(r->x,   	/* x = start from leftmost */
                        r->y+(r->height *2/3), /* y = a few pixels from the top */
                        r->width,       /* width = same width with the face */
                        r->height/3     /* height = 1/3 of face height */
                       )
                );
    mouth = cvHaarDetectObjects(img,	/* the source image, with estimated mouth */
                                cascade_mouth,  /* the mouth classifier */
                                storage,        /* memory buffer */
                                1.15, 4, 0,     /* tunable */
                                cvSize(25, 15)  /* minimum detection scale */
                               );
	int i;
        for( i = 0; i < (mouth ? mouth->total : 0); i++ )
        {
     
          CvRect *mouth_cord = (CvRect*)cvGetSeqElem(mouth, i);
          /* draw a white rectangle where the mouth is*/
          cvRectangle(img,
                      cvPoint(mouth_cord->x, mouth_cord->y),
                      cvPoint(mouth_cord->x + mouth_cord->width, mouth_cord->y + mouth_cord->height),
                      CV_RGB(255,255, 255),
                      1, 8, 0
                    );

	  // Save the mouth rectangle coordinates to the editor file
	  // in the proper format
	if (mouth_written == 0) {
		rec2edfile(mouth_cord,r);
		mouth_written = 1;	
	}
	}
}

/*Nose detection*/
void detectNose( IplImage *img,CvRect *r)
{
  CvSeq *nose;
 
  //nose detection- set region of interest (ROI)
  cvSetImageROI(img,                    /* the source image */
                cvRect(r->x,            /* x = start from leftmost */
                       r->y , 		/* y = the top */
                       r->width,        /* width = same width with the face */
                       r->height  	/* height = same height as face */
                      )
               );
         

  nose = cvHaarDetectObjects(img, 		/* the source image, with the estimated location defined */
                             cascade_nose,      /* the nose classifier */
                             storage,        	/* memory buffer */
                             1.15, 3, 0,     	/* tunable */
                             cvSize(25, 15)  	/* minimum detection scale */
                            );

  int i;
  for( i = 0; i < (nose ? nose->total : 0); i++ )
      {
          CvRect *nose_cord = (CvRect*)cvGetSeqElem(nose, i);

          /* draw a green rectangle where the nose is*/
          cvRectangle(img,
                      cvPoint(nose_cord->x, nose_cord->y),
                      cvPoint(nose_cord->x + nose_cord->width, nose_cord->y + nose_cord->height),
                      CV_RGB(0,255, 0),
                      1, 8, 0
                    );
	
	  // Save the nose rectangle coordinates to the editor file
	  // in the proper format
	if (nose_written == 0) {
		rec2edfile(nose_cord,r);
		nose_written=1;
	}
	}
}

/*Eyes detection*/
void detectEyes( IplImage *img,CvRect *r)
{
    char *eyecascade;
    CvSeq *eyes;
    int eye_detect=0;
   

   //eye detection starts
  /* Set the Region of Interest: estimate the eyes' position */
   
    cvSetImageROI(img,                    /* the source image */
          cvRect
          (
              r->x,            		/* x = start from leftmost */
              r->y + (r->height/5.5), 	/* y = a few pixels from the top */
              r->width,        		/* width = same width with the face */
              r->height/3.0    		/* height = 1/3 of face height */
          )
      );

      /* detect the eyes */
      eyes = cvHaarDetectObjects( img,            /* the source image, with the estimated location defined */
                                  cascade_eyes,   /* the eye classifier */
                                  storage,        /* memory buffer */
                                  1.15, 3, 0,     /* tunable */
                                  cvSize(25, 15)  /* minimum detection scale */
                                );
     
	if (eyes->total <= 0)
		printf("No eyes detected--editor file is likely to crash\n");

        /* draw a rectangle for each detected eye */
	int i;        
	for( i = 0; i < (eyes ? eyes->total : 0); i++ )
          {
              eye_detect++;
              /* get one eye */
              CvRect *eye = (CvRect*)cvGetSeqElem(eyes, i);
              /* draw a blue rectangle for the set of eyes */
                        cvRectangle(img,
                                    cvPoint(eye->x, eye->y),
                                    cvPoint(eye->x + eye->width, eye->y + eye->height),
                                    CV_RGB(0, 0, 255),
                                    1, 8, 0
                                   );

		// Save the eyes rectangle coordinates to the editor file
	  	// in the proper format
	if (bgnum == 0 && eyes_written == 0){
		//fprintf( editorFile, "background1=%s\n", srcFilename);
		fprintf( editorFile, "background1=fussell_young.jpg\n");
		rec2edfile(eye,r);
		eyes_written=1;
	}
	else if (bgnum == 1 && eyes_written == 0) {
		//fprintf( editorFile2, "background2=%s\n", dstFilename);
		fprintf( editorFile2, "background2=fussell_old.jpg\n");
		rec2edfile(eye,r);
		eyes_written=1;
	}
	else
		printf("Error, bgnum not valid or too many eyes, won't write");
	}
}

void detectFacialFeatures(IplImage *img, IplImage *temp_img, int imgSource)
{
    char image[100],msg[100],temp_image[100];
    float m[6];
    double factor = 1;
    CvMat M = cvMat( 2, 3, CV_32F, m );
    int w = (img)->width;
    int h = (img)->height;
    CvSeq* faces;
    CvRect *r;

    bgnum = imgSource;  // 0 for source, 1 for dest
    m[0] = (float)(factor*cos(0.0));
    m[1] = (float)(factor*sin(0.0));
    m[2] = w*0.5f;
    m[3] = -m[1];
    m[4] = m[0];
    m[5] = h*0.5f;
   
    cvGetQuadrangleSubPix(img, temp_img, &M);
    CvMemStorage* storage=cvCreateMemStorage(0);
    cvClearMemStorage( storage );
   
    if( cascade )
        faces = cvHaarDetectObjects(img,cascade, storage, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(20, 20));
    else
        printf("\nFrontal face cascade not loaded\n");

    /* for each face found, draw a red box */
    int i;
    for(i = 0 ; i < ( faces ? faces->total : 0 ) ; i++ )
    {       
        r = ( CvRect* )cvGetSeqElem( faces, i );
        cvRectangle( img,cvPoint( r->x, r->y ),cvPoint( r->x + r->width, r->y + r->height ),
                     CV_RGB( 255, 0, 0 ), 1, 8, 0 );   
   
        printf("\n face_x=%d face_y=%d wd=%d ht=%d\n",r->x,r->y,r->width,r->height);
      
	// Detect features and erase ROI in between
        detectEyes(img,r);
        cvResetImageROI(img);
        detectNose(img,r);
        cvResetImageROI(img);
        detectMouth(img,r);
        cvResetImageROI(img);
    }
    /* reset region of interest */
      cvResetImageROI(img);

      if(faces->total>0)
        {
	// Array for defining characteristics of JPG output
	// See http://stackoverflow.com/questions/801054/opencv-cvsaveimage-jpeg-compression-factor
	int p[3];
	p[0] = CV_IMWRITE_JPEG_QUALITY;
	p[1] = 100;
    	p[2] = 0;

	    if (bgnum == 0)
            	cvSaveImage( "./CorrespondenceEditor/face_detected.jpg", img, p );
	    else
		cvSaveImage( "./CorrespondenceEditor/face_detected2.jpg", img, p );
        }
}

/* --------------------------------
*  END OPENCV FEATURES
*  -------------------------------- */

int main(int argc, char **argv) {

	// Needed for OpenCV face detection
	CvCapture *capture;
	IplImage *img, *temp_img;
	
	morphContext context;

        switch(argc) {
                case 7:
			// Use automatic face detection? 0/1
			faceDetect = atoi(argv[6]);			

                        // start ugly string stuff
                        srcFilename = argv[1];
                        dstFilename = argv[2];
                        finalFilename = argv[5];

                        vecsFilename = argv[3];
                        // end ugly string stuff

                        percent = (float)atoi(argv[4])/(float)100;

			if (faceDetect == 1) {
				printf("Using Automatic Face Detection\n");

				// Remove old guide lines and setup new files
				remove("./CorrespondenceEditor/faceLines.txt");
				remove("./CorrespondenceEditor/faceLines2.txt");
				editorFile=fopen("./CorrespondenceEditor/faceLines.txt", "a");
				editorFile2=fopen("./CorrespondenceEditor/faceLines2.txt", "a");

				// load the classifiers
				storage = cvCreateMemStorage(0);
				cascade = (CvHaarClassifierCascade*)cvLoad(face_cascade,0,0,0);
				cascade_eyes = (CvHaarClassifierCascade*)cvLoad(eye_cascade,0,0,0);
				cascade_nose = (CvHaarClassifierCascade*)cvLoad(nose_cascade,0,0,0);
				cascade_mouth = (CvHaarClassifierCascade*)cvLoad(mouth_cascade,0,0,0);

				if( !(cascade || cascade_eyes ||cascade_nose||cascade_mouth) )
        			{
        				printf( "ERROR: Could not load classifier cascade\n" );
        				return -1;
        			}
	       
				// Perform face detection on the source image and save the features
				img=cvLoadImage(srcFilename,1);
				temp_img=cvLoadImage(srcFilename,1);
				eyes_written = 0; 
				nose_written = 0; 
				mouth_written = 0;		     
  
				if(!img)
				{
					printf("Could not load image file");
				}
				printf("\n curr_image = %s",srcFilename);
			     
				detectFacialFeatures(img,temp_img,0);
   
    				cvReleaseImage(&img);
    				cvReleaseImage(&temp_img);

				// Perform face detection on the destination image and save the features
				img=cvLoadImage(dstFilename,1);
				temp_img=cvLoadImage(dstFilename,1);
				eyes_written = 0; 
				nose_written = 0; 
				mouth_written = 0;
			       
				if(!img)
				{
					printf("Could not load image file");
				}
				printf("\n curr_image = %s",dstFilename);
			     
				detectFacialFeatures(img,temp_img,1);

				// Release the memory
				cvReleaseHaarClassifierCascade( &cascade );
    				cvReleaseHaarClassifierCascade( &cascade_eyes );
   
    				cvReleaseHaarClassifierCascade( &cascade_nose );
    				cvReleaseHaarClassifierCascade( &cascade_mouth );
    				cvReleaseMemStorage( &storage );
   
    				cvReleaseImage(&img);
    				cvReleaseImage(&temp_img);

				// Append the two editor files together into one
				fclose(editorFile2);
				editorFile2=fopen("./CorrespondenceEditor/faceLines2.txt", "r");
				setvbuf(editorFile2, NULL, _IOFBF, 1024 * 16);
				setvbuf(editorFile, NULL, _IOFBF, 1024 * 16);

				while (fgets(filecopy, sizeof filecopy, editorFile2)) {
					fputs(filecopy, editorFile);
				}
	
				// Change vecsfilename to reflect the new guide vectors
				vecsFilename="./CorrespondenceEditor/faceLines.txt";
				printf("vecsFilename:  %s\n", vecsFilename);
				
			}

			// Perform the actual morphing
			// If face detection was used, use the facial features as the algorithm guidelines
			// Otherwise use a provided file
                        printf("\nMorphing '%s' to '%s' @ %d%c \n",
                                        srcFilename, dstFilename, (int)(percent*100),'%');

                        // start clock
                        long t0 = clock();

                        if(!initContext(srcFilename, dstFilename, vecsFilename, &context)) {
                                return  0;
                        }

                        getIntermedVecs(&context, percent);
                        FIBITMAP *srcpic = warp(context.srcPic, context.src_vecs ,
							&context, 1, "foo1.bmp");
                        FIBITMAP *dstpic = warp(context.dstPic, context.dst_vecs ,
							&context, 1, "foo2.bmp");
                        FIBITMAP *final = crossDissolve(srcpic, dstpic, percent);
                        //FreeImage_Save(FIF_BMP, final, "final.bmp", BMP_DEFAULT);
                        if(!GenericWriter(final, finalFilename, 0)) {
                                DIE("Error");
                                return 0;
                        }
                        cleanContext(&context);

                        // end clock
                        long t1 = clock();

                        printf("Run time: %5.2f sec \n",(t1-t0)/(float)CLOCKS_PER_SEC);

                        break;
                default:
                        printf("Usage: %s <src filename> <dest filename> <vectors filename> <percent> <output filename> <automatic face detection = 0/1>\n", argv[0]);
                        return 0;
        }


        return 0;
}

